//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2020-06-15     ZhangYihua   first version
//
// Description  : transferring data between two clock domains with lower rate. The
//                min gap between two transmission is about (SYNC_NUM_D2S+1) clk_src
//                + (SYNC_NUM_S2D+1) clk_dst clock steps.
//                Async-FIFO is better choice if higher rate needed.
//################################################################################

module sync_bus #(
parameter           DATA_BW                 = 18,
parameter           SYNC_NUM_D2S            = 3,
parameter           SYNC_NUM_S2D            = 3
) ( 
input                                       rst_src_n,
input                                       clk_src,

input                                       src_vld,    // combination logic input is allowed
input               [DATA_BW-1:0]           src_data,   // combination logic input is allowed
output                                      src_rdy,

input                                       rst_dst_n,
input                                       clk_dst,

output  reg                                 dst_vld,
output  reg         [DATA_BW-1:0]           dst_data,
input                                       dst_rdy
);

//################################################################################
// define local varialbe and localparam
//################################################################################
wire                                        dst_tog_d2s;
wire                                        src_done;
reg                                         src_tog;
reg                 [DATA_BW-1:0]           src_data_hold;
wire                                        src_tog_s2d;
wire                                        dst_start;
wire                                        dst_done;
reg                                         dst_tog;

//################################################################################
// main
//################################################################################

sync_dff #(
        .SYNC_NUM                       (SYNC_NUM_D2S                   ),
        .BW                             (1                              ),
        .INI                            (1'b0                           )
) u_tog_d2s ( 
        .rst_n                          (rst_src_n                      ),
        .clk                            (clk_src                        ),

        .d                              (dst_tog                        ),
        .q                              (dst_tog_d2s                    )
);

assign src_rdy  = (src_tog==dst_tog_d2s) ? 1'b1 : 1'b0;
assign src_done = src_rdy & src_vld;
always@(posedge clk_src or negedge rst_src_n) begin
    if (rst_src_n==1'b0) begin
        src_tog       <=`U_DLY 1'b0;
        src_data_hold <=`U_DLY {DATA_BW{1'b0}};
    end else begin
        if (src_done==1'b1) begin
            src_tog       <=`U_DLY ~src_tog;
            src_data_hold <=`U_DLY src_data;
        end else
            ;
    end
end

// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------

sync_dff #(
        .SYNC_NUM                       (SYNC_NUM_S2D                   ),
        .BW                             (1                              ),
        .INI                            (1'b0                           )
) u_tog_s2d ( 
        .rst_n                          (rst_dst_n                      ),
        .clk                            (clk_dst                        ),

        .d                              (src_tog                        ),
        .q                              (src_tog_s2d                    )
);

assign dst_start = (dst_tog ^ src_tog_s2d) & ((~dst_vld) | dst_rdy); 
assign dst_done  = dst_vld & dst_rdy;
always@(posedge clk_dst or negedge rst_dst_n) begin
    if (rst_dst_n==1'b0) begin
        dst_vld  <=`U_DLY 1'b0;
        dst_tog  <=`U_DLY 1'b0;
        dst_data <=`U_DLY {DATA_BW{1'b0}};
    end else begin
        if (dst_start==1'b1) begin
            dst_vld  <=`U_DLY 1'b1;
            dst_tog  <=`U_DLY ~dst_tog;
            dst_data <=`U_DLY src_data_hold;
        end else if (dst_done==1'b1)
            dst_vld  <=`U_DLY 1'b0;
        else
            ;
    end
end

//################################################################################
// ASSERTION
//################################################################################

`ifdef CBB_ASSERT_ON
// synopsys translate_off


// generally define CBB_DEBUG_ON only when debugging CBB
`ifdef CBB_DEBUG_ON

a_dst_data: assert property (@(posedge clk_dst) disable iff (!rst_dst_n)
    (dst_vld&($stable(dst_vld))&(!$past(dst_rdy)) |-> $stable(dst_data))
) else begin
    $error("dst data is not stable.");
    $stop;
end

`endif

// synopsys translate_on
`endif

endmodule
